JavaScript Proxyãã³ãã©ãŒã®ããã©ãŒãã³ã¹ã培åºçã«è§£èª¬ããã€ã³ã¿ãŒã»ãããªãŒããŒãããã®æå°åãšãæ¬çªç°å¢åãã³ãŒãã®æé©åã«çŠç¹ãåœãŠãŸãããã¹ããã©ã¯ãã£ã¹ãé«åºŠãªãã¯ããã¯ãããã©ãŒãã³ã¹ãã³ãããŒã¯ãåŠã³ãŸãããã
JavaScript Proxyãã³ãã©ãŒã®ããã©ãŒãã³ã¹ïŒã€ã³ã¿ãŒã»ãããªãŒããŒãããã®æé©å
JavaScript Proxyã¯ãã¡ã¿ããã°ã©ãã³ã°ã®ããã®åŒ·åãªã¡ã«ããºã ãæäŸããéçºè ãåºæ¬çãªãªããžã§ã¯ãæäœãã€ã³ã¿ãŒã»ããããŠã«ã¹ã¿ãã€ãºã§ããããã«ããŸãããã®æ©èœã«ãããããŒã¿æ€èšŒã倿Žè¿œè·¡ãé å»¶ããŒããªã©ã®é«åºŠãªãã¿ãŒã³ãå®çŸããŸãããã ããã€ã³ã¿ãŒã»ããã®æ§è³ªäžãããã©ãŒãã³ã¹ã®ãªãŒããŒããããçºçããŸããProxyã广çã«æŽ»çšããããã©ãŒãã³ã¹ã®é«ãã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããã«ã¯ããã®ãªãŒããŒããããçè§£ãã軜æžããããšãéèŠã§ãã
JavaScript Proxyã®çè§£
Proxyãªããžã§ã¯ãã¯ãå¥ã®ãªããžã§ã¯ãïŒã¿ãŒã²ããïŒãã©ãããããã®ã¿ãŒã²ããã«å¯ŸããŠå®è¡ãããæäœãã€ã³ã¿ãŒã»ããããŸããProxyãã³ãã©ãŒã¯ããããã®ã€ã³ã¿ãŒã»ãããããæäœã®åŠçæ¹æ³ãå®çŸ©ããŸããåºæ¬çãªæ§æã«ã¯ãã¿ãŒã²ãããªããžã§ã¯ããšãã³ãã©ãŒãªããžã§ã¯ãã䜿çšããŠProxyã€ã³ã¹ã¿ã³ã¹ãäœæããããšãå«ãŸããŸãã
äŸïŒåºæ¬çãªProxy
const target = { name: 'John Doe' };
const handler = {
get: function(target, prop, receiver) {
console.log(`Getting property ${prop}`);
return Reflect.get(target, prop, receiver);
},
set: function(target, prop, value, receiver) {
console.log(`Setting property ${prop} to ${value}`);
return Reflect.set(target, prop, value, receiver);
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // Output: Getting property name, John Doe
proxy.age = 30; // Output: Setting property age to 30
console.log(target.age); // Output: 30
ãã®äŸã§ã¯ã`proxy`ãªããžã§ã¯ãã®ããããã£ã«ã¢ã¯ã»ã¹ãŸãã¯å€æŽããããšãããã³ã«ããããã`get`ãŸãã¯`set`ãã³ãã©ãŒãããªã¬ãŒãããŸãã`Reflect` APIã¯ãæäœãå ã®ã¿ãŒã²ãããªããžã§ã¯ãã«è»¢éããæ¹æ³ãæäŸããããã©ã«ãã®åäœãç¶æãããããã«ããŸãã
Proxyãã³ãã©ãŒã®ããã©ãŒãã³ã¹ãªãŒããŒããã
Proxyã®ããã©ãŒãã³ã¹ã«é¢ããäž»ãªèª²é¡ã¯ã鿥åç §ã®å±€ã远å ãããããšã§ããProxyãªããžã§ã¯ãã«å¯Ÿããåæäœã¯ããã³ãã©ãŒé¢æ°ãå®è¡ããããšãå«ã¿ãCPUãµã€ã¯ã«ãæ¶è²»ããŸãããã®ãªãŒããŒãããã®é倧床ã¯ãããã€ãã®èŠå ã«ãã£ãŠç°ãªããŸãã
- ãã³ãã©ãŒé¢æ°ã®è€éãïŒãã³ãã©ãŒé¢æ°å ã®ããžãã¯ãè€éã§ããã»ã©ããªãŒããŒãããã¯å€§ãããªããŸãã
- ã€ã³ã¿ãŒã»ãããããæäœã®é »åºŠïŒProxyã倿°ã®æäœãã€ã³ã¿ãŒã»ããããå Žåã环ç©çãªãªãŒããŒãããã倧ãããªããŸãã
- JavaScriptãšã³ãžã³ã®å®è£ ïŒç°ãªãJavaScriptãšã³ãžã³ïŒäŸïŒV8ãSpiderMonkeyãJavaScriptCoreïŒã¯ãProxyã®æé©åã¬ãã«ãç°ãªãå ŽåããããŸãã
Proxyã䜿çšããŠããªããžã§ã¯ãã«æžã蟌ãŸããåã«ããŒã¿ãæ€èšŒããã·ããªãªãèããŠãã ããããã®æ€èšŒã«è€éãªæ£èŠè¡šçŸãŸãã¯å€éšAPIåŒã³åºããå«ãŸããå Žåãç¹ã«ããŒã¿ãé »ç¹ã«æŽæ°ãããå Žåã¯ããªãŒããŒãããã倧ãããªãå¯èœæ§ããããŸãã
Proxyãã³ãã©ãŒã®ããã©ãŒãã³ã¹ãæé©åããããã®æŠç¥
JavaScript Proxyãã³ãã©ãŒã«é¢é£ããããã©ãŒãã³ã¹ãªãŒããŒããããæå°éã«æããããã«ãããã€ãã®æŠç¥ãæ¡çšã§ããŸãã
1. ãã³ãã©ãŒã®è€éããæå°éã«æãã
ãªãŒããŒããããåæžããæãçŽæ¥çãªæ¹æ³ã¯ããã³ãã©ãŒé¢æ°å ã®ããžãã¯ãç°¡çŽ åããããšã§ããäžèŠãªèšç®ãè€éãªããŒã¿æ§é ãããã³å€éšäŸåé¢ä¿ãé¿ããŠãã ããããã³ãã©ãŒé¢æ°ããããã¡ã€ã«ããŠãããã©ãŒãã³ã¹ã®ããã«ããã¯ãç¹å®ããããã«å¿ããŠæé©åããŸãã
äŸïŒããŒã¿æ€èšŒã®æé©å
ããããã£ãèšå®ããããã³ã«è€éãªãªã¢ã«ã¿ã€ã æ€èšŒãå®è¡ãã代ããã«ãããå®äŸ¡ãªäºåãã§ãã¯ã䜿çšããããŒã¿ããŒã¹ã«ããŒã¿ãä¿åããåãªã©ãåŸã®æ®µéã«å®å šãªæ€èšŒãå»¶æããããšãæ€èšããŠãã ããã
const target = {};
const handler = {
set: function(target, prop, value) {
// Simple type check (example)
if (typeof value !== 'string') {
console.warn(`Invalid value for property ${prop}: ${value}`);
return false; // Prevent setting the value
}
target[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
ãã®æé©åãããäŸã§ã¯ãåºæ¬çãªåãã§ãã¯ãå®è¡ããŸããããè€éãªæ€èšŒã¯å»¶æã§ããŸãã
2. ã¿ãŒã²ãããçµã£ãã€ã³ã¿ãŒã»ããã®äœ¿çš
ãã¹ãŠã®æäœãã€ã³ã¿ãŒã»ãããã代ããã«ãã«ã¹ã¿ã åäœãå¿ èŠãªæäœã®ã¿ãã€ã³ã¿ãŒã»ããããããšã«çŠç¹ãåœãŠãŸããããšãã°ãç¹å®ã®ããããã£ãžã®å€æŽã®ã¿ã远跡ããå¿ èŠãããå Žåã¯ããããã®ããããã£ã®`set`æäœã®ã¿ãã€ã³ã¿ãŒã»ãããããã³ãã©ãŒãäœæããŸãã
äŸïŒã¿ãŒã²ãããçµã£ãããããã£è¿œè·¡
const target = { name: 'John Doe', age: 30 };
const trackedProperties = new Set(['age']);
const handler = {
set: function(target, prop, value) {
if (trackedProperties.has(prop)) {
console.log(`Property ${prop} changed from ${target[prop]} to ${value}`);
}
target[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
proxy.name = 'Jane Doe'; // No log
proxy.age = 31; // Output: Property age changed from 30 to 31
ãã®äŸã§ã¯ã`age`ããããã£ãžã®å€æŽã®ã¿ãèšé²ãããä»ã®ããããã£ã®å²ãåœãŠã®ãªãŒããŒããããåæžãããŸãã
3. Proxyã®ä»£æ¿æ¡ã®æ€èš
Proxyã¯åŒ·åãªã¡ã¿ããã°ã©ãã³ã°æ©èœãæäŸããŸãããåžžã«æãããã©ãŒãã³ã¹ã®é«ããœãªã¥ãŒã·ã§ã³ã§ãããšã¯éããŸãããçŽæ¥ããããã£ã¢ã¯ã»ãµãŒïŒã²ãã¿ãŒãšã»ãã¿ãŒïŒããŸãã¯ã«ã¹ã¿ã ã€ãã³ãã·ã¹ãã ãªã©ã®ä»£æ¿ã¢ãããŒãã§ãããäœããªãŒããŒãããã§ç®çã®æ©èœãå®çŸã§ãããã©ãããè©äŸ¡ããŸãã
äŸïŒã²ãã¿ãŒãšã»ãã¿ãŒã®äœ¿çš
class Person {
constructor(name, age) {
this._name = name;
this._age = age;
}
get name() {
return this._name;
}
set name(value) {
console.log(`Name changed to ${value}`);
this._name = value;
}
get age() {
return this._age;
}
set age(value) {
if (value < 0) {
throw new Error('Age cannot be negative');
}
this._age = value;
}
}
const person = new Person('John Doe', 30);
person.name = 'Jane Doe'; // Output: Name changed to Jane Doe
try {
person.age = -10; // Throws an error
} catch (error) {
console.error(error.message);
}
ãã®äŸã§ã¯ãã²ãã¿ãŒãšã»ãã¿ãŒã¯ãProxyã®ãªãŒããŒããããªãã«ããããã£ã¢ã¯ã»ã¹ãšå€æŽãå¶åŸ¡ããŸãããã®ã¢ãããŒãã¯ãã€ã³ã¿ãŒã»ããã®ããžãã¯ãæ¯èŒçåçŽã§ãåã ã®ããããã£ã«åºæã§ããå Žåã«é©ããŠããŸãã
4. ãããŠã³ã¹ãšã¹ããããªã³ã°
Proxyãã³ãã©ãŒãããã«å®è¡ããå¿ èŠã®ãªãã¢ã¯ã·ã§ã³ãå®è¡ããå Žåã¯ããããŠã³ã¹ãŸãã¯ã¹ããããªã³ã°ææ³ã䜿çšããŠããã³ãã©ãŒã®åŒã³åºãé »åºŠãæžããããšãæ€èšããŠãã ãããããã¯ããŠãŒã¶ãŒå ¥åãé »ç¹ãªããŒã¿æŽæ°ãå«ãã·ããªãªã§ç¹ã«åœ¹ç«ã¡ãŸãã
äŸïŒæ€èšŒé¢æ°ã®ãããŠã³ã¹
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
const target = {};
const handler = {
set: function(target, prop, value) {
const validate = debounce(() => {
console.log(`Validating ${prop}: ${value}`);
// Perform validation logic here
}, 250); // Debounce for 250 milliseconds
target[prop] = value;
validate();
return true;
}
};
const proxy = new Proxy(target, handler);
proxy.name = 'John';
proxy.name = 'Johnny';
proxy.name = 'Johnathan'; // Validation will only run after 250ms of inactivity
ãã®äŸã§ã¯ã`validate`颿°ããããŠã³ã¹ããã`name`ããããã£ãçæéã§è€æ°åæŽæ°ãããå Žåã§ããéã¢ã¯ãã£ãæéã®250ããªç§åŸã«1åã ãå®è¡ãããããã«ããŸãã
5. çµæã®ãã£ãã·ã¥
ãã³ãã©ãŒããåãå ¥åã«å¯ŸããŠåãçµæãçæããèšç®ã³ã¹ãã®é«ãæäœãå®è¡ããå Žåã¯ãçµæããã£ãã·ã¥ããŠãåé·ãªèšç®ãåé¿ããããšãæ€èšããŠãã ãããåçŽãªãã£ãã·ã¥ãªããžã§ã¯ããŸãã¯ããé«åºŠãªãã£ãã·ã¥ã©ã€ãã©ãªã䜿çšããŠã以åã«èšç®ãããå€ãä¿åããã³ååŸããŸãã
äŸïŒAPIã¬ã¹ãã³ã¹ã®ãã£ãã·ã¥
const cache = {};
const target = {};
const handler = {
get: async function(target, prop) {
if (cache[prop]) {
console.log(`Fetching ${prop} from cache`);
return cache[prop];
}
console.log(`Fetching ${prop} from API`);
const response = await fetch(`/api/${prop}`); // Replace with your API endpoint
const data = await response.json();
cache[prop] = data;
return data;
}
};
const proxy = new Proxy(target, handler);
(async () => {
console.log(await proxy.users); // Fetches from API
console.log(await proxy.users); // Fetches from cache
})();
ãã®äŸã§ã¯ã`users`ããããã£ãAPIãããã§ãããããŸããã¬ã¹ãã³ã¹ã¯ãã£ãã·ã¥ããããããåŸç¶ã®ã¢ã¯ã»ã¹ã§ã¯ãå¥ã®APIåŒã³åºããè¡ã代ããã«ããã£ãã·ã¥ããããŒã¿ãååŸãããŸãã
6. äžå€æ§ãšæ§é å ±æ
è€éãªããŒã¿æ§é ãæ±ãå Žåã¯ãäžå€ããŒã¿æ§é ãšæ§é å ±æææ³ã®äœ¿çšãæ€èšããŠãã ãããäžå€ããŒã¿æ§é ã¯ã€ã³ãã¬ãŒã¹ã§å€æŽãããŸããã代ããã«ã倿Žã«ãã£ãŠæ°ããããŒã¿æ§é ãäœæãããŸããæ§é å ±æã«ããããããã®æ°ããããŒã¿æ§é ã¯ãå ã®ããŒã¿æ§é ãšå ±ééšåãå ±æããã¡ã¢ãªå²ãåœãŠãšã³ããŒãæå°éã«æããããšãã§ããŸããImmutable.jsãImmerãªã©ã®ã©ã€ãã©ãªã¯ãäžå€ããŒã¿æ§é ãšæ§é å ±ææ©èœãæäŸããŸãã
äŸïŒImmerãProxyã§äœ¿çšãã
import { produce } from 'immer';
const baseState = { name: 'John Doe', address: { street: '123 Main St' } };
const handler = {
set: function(target, prop, value) {
const nextState = produce(target, draft => {
draft[prop] = value;
});
// Replace the target object with the new immutable state
Object.assign(target, nextState);
return true;
}
};
const proxy = new Proxy(baseState, handler);
proxy.name = 'Jane Doe'; // Creates a new immutable state
console.log(baseState.name); // Output: Jane Doe
ãã®äŸã§ã¯ãImmerã䜿çšããŠãããããã£ã倿Žããããã³ã«äžå€ç¶æ ãäœæããŸããProxyã¯setæäœãã€ã³ã¿ãŒã»ããããæ°ããäžå€ç¶æ ã®äœæãããªã¬ãŒããŸããããè€éã§ãããçŽæ¥çãªå€æŽãåé¿ããŸãã
7. Proxyã®åãæ¶ã
ProxyãäžèŠã«ãªã£ãå Žåã¯ãé¢é£ãããªãœãŒã¹ãè§£æŸããããã«åãæ¶ããŸããProxyãåãæ¶ããšãProxyãä»ããã¿ãŒã²ãããªããžã§ã¯ããšã®ãã以äžã®ããåãã鲿¢ãããŸãã`Proxy.revocable()`ã¡ãœããã¯ã`revoke()`颿°ãæäŸããåãæ¶ãå¯èœãªProxyãäœæããŸãã
äŸïŒProxyã®åãæ¶ã
const { proxy, revoke } = Proxy.revocable({}, {
get: function(target, prop) {
return 'Hello';
}
});
console.log(proxy.message); // Output: Hello
revoke();
try {
console.log(proxy.message); // Throws a TypeError
} catch (error) {
console.error(error.message); // Output: Cannot perform 'get' on a proxy that has been revoked
}
Proxyãåãæ¶ããšããªãœãŒã¹ãè§£æŸããããã以äžã®ã¢ã¯ã»ã¹ã鲿¢ãããŸããããã¯ãé·æéå®è¡ãããã¢ããªã±ãŒã·ã§ã³ã§éåžžã«éèŠã§ãã
Proxyããã©ãŒãã³ã¹ã®ãã³ãããŒã¯ãšãããã¡ã€ãªã³ã°
Proxyãã³ãã©ãŒã®ããã©ãŒãã³ã¹ãžã®åœ±é¿ãè©äŸ¡ããæã广çãªæ¹æ³ã¯ãçŸå®çãªç°å¢ã§ã³ãŒãããã³ãããŒã¯ããã³ãããã¡ã€ã«ããããšã§ããChrome DevToolsãNode.js InspectorããŸãã¯å°çšã®ãã³ãããŒã¯ã©ã€ãã©ãªãªã©ã®ããã©ãŒãã³ã¹ãã¹ãããŒã«ã䜿çšããŠãããŸããŸãªã³ãŒããã¹ã®å®è¡æéãæž¬å®ããŸãããã³ãã©ãŒé¢æ°ã§è²»ããããæéã«æ³šæããæé©åã®é åãç¹å®ããŸãã
äŸïŒãããã¡ã€ãªã³ã°ã«Chrome DevToolsã䜿çšãã
- Chrome DevToolsãéããŸãïŒCtrl + Shift + IãŸãã¯Cmd + Option + IïŒã
- [ããã©ãŒãã³ã¹]ã¿ãã«ç§»åããŸãã
- é²ç»ãã¿ã³ãã¯ãªãã¯ããProxyã䜿çšããã³ãŒããå®è¡ããŸãã
- é²ç»ã忢ããŸãã
- ãã¬ãŒã ãã£ãŒããåæããŠããã³ãã©ãŒé¢æ°ã®ããã©ãŒãã³ã¹ã®ããã«ããã¯ãç¹å®ããŸãã
çµè«
JavaScript Proxyã¯ããªããžã§ã¯ãæäœãã€ã³ã¿ãŒã»ããããŠã«ã¹ã¿ãã€ãºããããã®åŒ·åãªæ¹æ³ãæäŸããé«åºŠãªã¡ã¿ããã°ã©ãã³ã°ãã¿ãŒã³ãå¯èœã«ããŸãããã ããåºæã®ã€ã³ã¿ãŒã»ãããªãŒããŒãããã«ã¯ãæ éãªæ€èšãå¿ èŠã§ãããã³ãã©ãŒã®è€éããæå°éã«æããã¿ãŒã²ãããçµã£ãã€ã³ã¿ãŒã»ããã䜿çšãã代æ¿ã¢ãããŒããæ€èšãããããŠã³ã¹ããã£ãã·ã¥ãäžå€æ§ãªã©ã®ææ³ã掻çšããããšã§ãProxyãã³ãã©ãŒã®ããã©ãŒãã³ã¹ãæé©åãããã®åŒ·åãªæ©èœã广çã«å©çšããããã©ãŒãã³ã¹ã®é«ãã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ã§ããŸãã
ããã©ãŒãã³ã¹ã®ããã«ããã¯ãç¹å®ããæé©åæŠç¥ã®æå¹æ§ãæ€èšŒããããã«ãã³ãŒãããã³ãããŒã¯ããã³ãããã¡ã€ã«ããããšãå¿ããªãã§ãã ãããProxyãã³ãã©ãŒã®å®è£ ãç¶ç¶çã«ç£èŠããã³æ¹è¯ããŠãæ¬çªç°å¢ã§æé©ãªããã©ãŒãã³ã¹ã確ä¿ããŠãã ãããæ éãªèšç»ãšæé©åã«ãããJavaScript Proxyã¯ãå ç¢ã§ä¿å®å¯èœãªã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã®è²ŽéãªããŒã«ã«ãªãå¯èœæ§ããããŸãã
ããã«ãææ°ã®JavaScriptãšã³ãžã³ã®æé©åã«é¢ããææ°æ å ±ãå ¥æããŠãã ãããææ°ã®ãšã³ãžã³ã¯åžžã«é²åããŠãããProxyã®å®è£ ã«å¯Ÿããæ¹åã¯ãããã©ãŒãã³ã¹ã«å€§ããªåœ±é¿ãäžããå¯èœæ§ããããŸããProxyã®äœ¿çšç¶æ³ãšæé©åæŠç¥ã宿çã«åè©äŸ¡ããŠããããã®é²æ©ã掻çšããŠãã ããã
æåŸã«ãã¢ããªã±ãŒã·ã§ã³ã®ããåºç¯ãªã¢ãŒããã¯ãã£ãæ€èšããŠãã ãããå Žåã«ãã£ãŠã¯ãProxyãã³ãã©ãŒã®ããã©ãŒãã³ã¹ãæé©åããã«ã¯ãããããã€ã³ã¿ãŒã»ããã®å¿ èŠæ§ãæžããããã«ãå šäœçãªèšèšãåèããå¿ èŠããããŸããé©åã«èšèšãããã¢ããªã±ãŒã·ã§ã³ã¯ãäžå¿ èŠãªè€éããæå°éã«æããå¯èœãªéããããåçŽã§å¹ççãªãœãªã¥ãŒã·ã§ã³ã«äŸåããŸãã